home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / c / pgmprot.com / SETVER.C < prev    next >
Encoding:
C/C++ Source or Header  |  1988-11-05  |  18.1 KB  |  703 lines

  1. /*************************************************************************
  2. *
  3. *    setver.c    Serialize an .EXE File
  4. *
  5. *    This program will insert user-defined serial and version numbers
  6. *    into the .EXE program file specified. It is not intended to be 
  7. *    used with .COM programs and will not work with anything other than 
  8. *    .EXE files. This is guaranteed by forcing the extension to .EXE.
  9. *
  10. *    The program operates by searching through the target file for the
  11. *    copyright message. Once found, the serial number block is located
  12. *    after the null byte of the copyright message. The copyright
  13. *    message is limited to MAXCPMSG, which is currently 1024 bytes.
  14. *
  15. *    Once the serial number block is located, the binary serial number
  16. *    is inserted after swapping bytes 2 and 4. Following this is the
  17. *    two-byte version number, and then a checksum byte that spans both 
  18. *    the copyright message and the serial/version numbers. The checksum
  19. *    is calculated by XORing each byte MOD 256. While simple to calculate,
  20. *    this method is difficult to decipher.
  21. *
  22. *    This program must be linked with the file SERNO.ASM, which is also
  23. *    used with any target program that is to be serialized. It is intended
  24. *    to be compiled using Microsoft C V5.1 or later.
  25. *
  26. *    The command-line format is:
  27. *
  28. *    SETVER <program filename> [-sSerial_Number] [-vVersion_Number]
  29. *
  30. *    The <program filename> may be any valid DOS pathname. The extension
  31. *    will be forced to .EXE. The [serial number] may be any numeric string
  32. *    of one to nine digits, and is optional. If omitted, it will default
  33. *    to zero. The [version number] may be any numeric string of up to five
  34. *    digits, provided that the integer value is less than 65536. It is
  35. *    optional and will default to zero if omitted. Alpha characters are 
  36. *    not allowed in the serial or version numbers. If both the serial AND
  37. *    version numbers are omitted, any serial and version numbers in the
  38. *    file will be displayed.
  39. *
  40. *    Arguments may be specified in any order.
  41. *
  42. *************************************************************************/
  43.  
  44. #include <stdio.h>
  45. #include <stdlib.h>
  46. #include <limits.h>
  47. #include <string.h>
  48. #include <ctype.h>
  49.  
  50. /*#page*******************************************************************
  51. *
  52. *    Constants
  53. *    
  54. *************************************************************************/
  55.  
  56. #define    MAXCPMSG    1024        /* maximum copyright message length */
  57. #define    MAXPATH        128        /* maximum length of pathname */
  58.  
  59. /*************************************************************************
  60. *
  61. *    Serial Number Block (follows NULL on copyright message)
  62. *    
  63. *************************************************************************/
  64.  
  65. #pragma pack(1)
  66.  
  67. typedef union __sn_t {
  68.     long    sni;            /* integer serial number */
  69.     char    snb [sizeof (long)];    /* component bytes */
  70.     } sn_t;
  71.  
  72. typedef struct __sn_block_t {
  73.     sn_t        SN_Data;    /* serial number */
  74.     unsigned int    VN_Data;    /* version number */
  75.     char        SN_Cksm;    /* copyright/number checksum */
  76.     char        EXE_Cksm;    /* .EXE file checksum correction */
  77.     } sn_block_t;
  78.  
  79. /*#page*******************************************************************
  80. *
  81. *    .EXE File Header Structure
  82. *    
  83. *************************************************************************/
  84.  
  85. typedef struct __exe_hdr_t {
  86.     int    MagicNumber;
  87.     int    LastPageSize;
  88.     int    PagesInFile;
  89.     int    NumberRelocations;
  90.     int    HeaderParagraphs;
  91.     int    ParagraphsNeeded;
  92.     int    ParagraphsWanted;
  93.     int    InitialSS;
  94.     int    InitialSP;
  95.     int    WordChecksum;
  96.     int    InitialIP;
  97.     int    InitialCS;
  98.     int    FirstRelocation;
  99.     int    OverlayNumber;
  100.     } exe_hdr_t;
  101.  
  102. /*************************************************************************
  103. *
  104. *    External Functions Provided by 'serno.asm'
  105. *    
  106. *************************************************************************/
  107.  
  108. extern char far *_CPPTR (void);        /* get far pointer to copyright message */
  109. extern int _CPSUM (char far *p);    /* calculate XOR checksum */
  110.  
  111. /*************************************************************************
  112. *
  113. *    Local Function Prototypes
  114. *    
  115. *************************************************************************/
  116.  
  117. void main (int argc, char **argv);
  118. int InsertNumbers (long pos, sn_t sn, unsigned int vn);
  119. int MoveTo (long pos);
  120. int ConvertSerialNum (char *cp, sn_t *sn);
  121. int ConvertVersionNum (char *cp, unsigned int *vn);
  122. int ConvertPathName (char *src, char *dst);
  123. long LocateCopyright (void);
  124. long LoadCopyright (long pos);
  125. void DisplayNumbers (long pos);
  126.  
  127. /*#page*******************************************************************
  128. *
  129. *    Variables
  130. *    
  131. *************************************************************************/
  132.  
  133. FILE        *fp;            /* .EXE file pointer */
  134. char        TargetName [MAXPATH+1];    /* .EXE file pathname */
  135. sn_block_t    *sn_block;        /* serial number block */
  136. char        *CpMsgPtr;        /* pointer to copyright message */
  137.  
  138. /*#page*******************************************************************
  139. *
  140. *    SETVER Main
  141. *    
  142. *************************************************************************/
  143.  
  144. void main (argc, argv)
  145. int argc;
  146. char *argv [];
  147.     {
  148.     sn_t sn;
  149.     unsigned int vn;
  150.     long pos;
  151.     char gotfile, display, *cp;
  152.     int arg;
  153.  
  154.     fprintf (stderr, "EXE Program File Distribution Setup Utility  Version 1.00\n");
  155.     fprintf (stderr, "Copyright (c) 1988 Quid Pro Quo Software. All rights reserved.\n");
  156.  
  157.     /* set up default numbers */
  158.  
  159.     sn.sni = 0L;
  160.     vn = 0;
  161.  
  162.     /* parse command line */
  163.  
  164.     display = 1;
  165.     gotfile = 0;
  166.     for (arg = 1; arg < argc; ++arg)    
  167.         {
  168.         if (*(cp = argv [arg]) == '-')
  169.             {
  170.             /* then serial or version number */
  171.  
  172.             display = 0;
  173.             ++cp;
  174.             if (toupper (*cp) == 'S')
  175.                 {
  176.                 /* convert desired serial number to binary */
  177.  
  178.                 if (! ConvertSerialNum (++cp, &sn))
  179.                     exit (1);
  180.                 }
  181.             else if (toupper (*cp) == 'V')
  182.                 {
  183.                 /* convert desired version number to binary */
  184.  
  185.                 if (! ConvertVersionNum (++cp, &vn))
  186.                     exit (1);
  187.                 }
  188.             else
  189.                 {
  190.                 fprintf (stderr, "Unknown argument: %s\n", argv [arg]);
  191.                 exit (1);
  192.                 }
  193.             }
  194.  
  195.         else if (! gotfile)
  196.             {
  197.             /* not switch - must be program filename */
  198.  
  199.             if (! ConvertPathName (cp, TargetName))
  200.                 exit (1);
  201.             gotfile = 1;
  202.             }
  203.         else
  204.             {
  205.             fprintf (stderr, "More than one program filename specified\n");
  206.             exit (1);
  207.             }
  208.         }
  209.  
  210.     if (! gotfile)
  211.         {
  212.         fprintf (stderr, "\nUsage: SETVER <.EXE Program Name> [-sSerialNumber] [-vVersionNumber]\n");
  213.         exit (1);
  214.         }
  215.  
  216.     /* open the target file */
  217.  
  218.     if ((fp = fopen (TargetName, "rb+")) == NULL)
  219.         {
  220.         fprintf (stderr, "\nCannot open file %s\n", TargetName);
  221.         exit (1);
  222.         }
  223.  
  224.     /* locate the copyright message */
  225.  
  226.     if ((pos = LocateCopyright ()) == 0L)
  227.         {
  228.         fclose (fp);
  229.         exit (1);
  230.         }
  231.  
  232.     /* check for modification or display mode */
  233.  
  234.     if (display)
  235.         DisplayNumbers (pos);
  236.  
  237.     else if (! InsertNumbers (pos, sn, vn))
  238.         {
  239.         fclose (fp);
  240.         exit (1);
  241.         }
  242.  
  243.     /* close the target file */
  244.  
  245.     fclose (fp);
  246.     if (! display)
  247.         fprintf (stderr, "\nProgram file %s successfully modified.\n", TargetName);
  248.  
  249.     exit (0);
  250.     }
  251.  
  252. /*#page*******************************************************************
  253. *
  254. *    Insert Serial Number
  255. *
  256. *    This function will insert the passed serial number into the target
  257. *    file. It first loads the copyright message and serial number block,
  258. *    and then writes the serial number into the buffer. The checksum
  259. *    is then calculated on the buffer, the .EXE checksum is corrected,
  260. *    and the serial number block is rewritten to the file.
  261. *
  262. *    Synopsis:
  263. *
  264. *        rc = InsertNumbers (pos, sn, vn);
  265. *
  266. *        int rc;            true if numbers successfully written
  267. *        long pos;        file offset to copyright message
  268. *        sn_t sn;        serial number to insert
  269. *        unsigned int vn;    version number to insert
  270. *        
  271. *************************************************************************/
  272.  
  273. int InsertNumbers (pos, sn, vn)
  274. long pos;
  275. sn_t sn;
  276. unsigned int vn;
  277.     {
  278.     char *cp;
  279.     int x;
  280.     unsigned char sum;
  281.  
  282.     /* allocate memory and read the copyright message and serial 
  283.        number block */
  284.  
  285.     if ((pos = LoadCopyright (pos)) == 0L)
  286.         return (0);
  287.  
  288.     /* swap bytes two and four and insert the serial number */
  289.  
  290.     x = sn.snb [1];
  291.     sn.snb [1] = sn.snb [3];
  292.     sn.snb [3] = (char) x;
  293.     sn_block->SN_Data.sni = sn.sni;
  294.  
  295.     /* insert the version number */
  296.  
  297.     sn_block->VN_Data = vn;
  298.  
  299.     /* now calculate the copyright and serial/version number XOR checksum */
  300.  
  301.     sn_block->SN_Cksm = (char) _CPSUM ((char far *) CpMsgPtr);
  302.  
  303.     /* now do a standard byte checksum on the serial number and XOR sum */
  304.  
  305.     for (x = sum = 0; x < (sizeof (sn_block_t) - 1); ++x)
  306.         sum += (unsigned char) *cp++;
  307.  
  308.     /* calculate the value needed for a zero .EXE checksum and insert it */
  309.  
  310.     sn_block->EXE_Cksm = (char) (0x100 - (unsigned int) sum);
  311.  
  312.     /* write the serial number block back to the file */
  313.  
  314.     if (! MoveTo (pos))
  315.         return (0);
  316.  
  317.     if (fwrite ((char *) sn_block, 1, sizeof (sn_block_t), fp) < sizeof (sn_block_t))
  318.         {
  319.         fprintf (stderr, "\nUnable to rewrite serial number block to file %s\n", TargetName);
  320.         return (0);
  321.         }
  322.  
  323.     /* release the work buffer */
  324.  
  325.     free (CpMsgPtr);
  326.     return (1);
  327.     }
  328.  
  329. /*#page*******************************************************************
  330. *
  331. *    Display Serial and Version Numbers
  332. *
  333. *    This function will display the serial and version numbers that
  334. *    exist in the target file. If the serial or version number is 
  335. *    invalid, an error message will be displayed. It is assumed that 
  336. *    serial and version numbers of zero indicate that the file has not 
  337. *    been serialized; otherwise, tampering will be assumed.
  338. *
  339. *    This function will not alter the target file.
  340. *
  341. *    Synopsis:
  342. *
  343. *        DisplayNumbers (pos);
  344. *
  345. *        long pos;    file offset to copyright message
  346. *        
  347. *************************************************************************/
  348.  
  349. void DisplayNumbers (pos)
  350. long pos;
  351.     {
  352.     int x;
  353.     unsigned int vn;
  354.  
  355.     /* read the copyright message and serial number block */
  356.  
  357.     if ((pos = LoadCopyright (pos)) != 0L)
  358.         {
  359.         /* verify the copyright and serial number checksum */
  360.  
  361.         if ((char) (x = _CPSUM ((char far *) CpMsgPtr)) == sn_block->SN_Cksm)
  362.             {
  363.             printf ("\nProgram File %s:\nCopyright: \"%s\"\n", TargetName, CpMsgPtr);
  364.         
  365.             /* swap bytes two and four and display the serial number */
  366.  
  367.             x = sn_block->SN_Data.snb [1];
  368.             sn_block->SN_Data.snb [1] = sn_block->SN_Data.snb [3];
  369.             sn_block->SN_Data.snb [3] = (char) x;
  370.  
  371.             printf ("Serial Number: %ld\n", sn_block->SN_Data.sni);
  372.  
  373.             /* display the version number */
  374.  
  375.             vn = sn_block->VN_Data;
  376.             printf ("Version Number: V%u.%02u\n", vn/100, vn%100);
  377.             }
  378.  
  379.         else if (sn_block->SN_Data.sni == 0L && 
  380.              sn_block->VN_Data == 0 &&
  381.              sn_block->SN_Cksm == 0 && 
  382.              sn_block->EXE_Cksm == 0)
  383.             printf ("\nFile %s has not been serialized.\n", TargetName);
  384.  
  385.         else
  386.             printf ("\nWARNING: PROGRAM FILE %s HAS BEEN ALTERED\n", TargetName);
  387.  
  388.         free (CpMsgPtr);
  389.         }
  390.     }
  391.  
  392. /*#page*******************************************************************
  393. *
  394. *    Read Copyright Message and Serial Number Block
  395. *
  396. *    This function will allocate memory for the copyright message and
  397. *    the serial number block, and then read this information into the
  398. *    allocated buffer at the position given by the input argument.
  399. *    It returns the file offset of the serial number block if the
  400. *    data are successfully read, or 0L if an error occurs.
  401. *
  402. *    The global pointer CpMsgPtr is set to point to the copyright 
  403. *    message read from the file, and the global pointer sn_block
  404. *    points to the serial number block.
  405. *
  406. *    Synopsis:
  407. *
  408. *        pos = LoadCopyright (pos);
  409. *
  410. *        long pos;    file offset of serial number block
  411. *    
  412. *************************************************************************/
  413.  
  414. long LoadCopyright (pos)
  415. long pos;
  416.     {
  417.     char *cp;
  418.     int c;
  419.  
  420.     /* allocate a buffer and read the copyright data into it */
  421.  
  422.     if ((CpMsgPtr = malloc ((unsigned int) (MAXCPMSG + sizeof (sn_block_t) + 1))) == NULL)
  423.         {
  424.         fprintf (stderr, "\nInsufficient memory\n");
  425.         return (0L);
  426.         }
  427.  
  428.     if (! MoveTo (pos))
  429.         return (0L);
  430.  
  431.     /* read copyright message into buffer */
  432.  
  433.     for (cp = CpMsgPtr; cp < &CpMsgPtr [MAXCPMSG] && (c = fgetc (fp)) != EOF && (char) c != '\0'; *cp++ = (char) c)
  434.         ;
  435.  
  436.     if (c == EOF)
  437.         {
  438.         fprintf (stderr, "\nUnexpected EOF; unterminated copyright message\n");
  439.         return (0L);
  440.         }
  441.  
  442.     if ((char) c != '\0')
  443.         {
  444.         fprintf (stderr, "\nCopyright message exceeds maximum of %d characters\n", MAXCPMSG);
  445.         return (0L);
  446.         }
  447.  
  448.     /* save the null byte in the buffer, and save the location of the
  449.        serial number block */
  450.  
  451.     *cp++ = (char) c;
  452.     sn_block = (sn_block_t *) cp;
  453.  
  454.     /* save the current file position; we need only rewrite the serial
  455.        number block */
  456.  
  457.     if ((pos = ftell (fp)) < 0L)
  458.         {
  459.         fprintf (stderr, "\nUnable to determine serial number block offset\n");
  460.         return (0L);
  461.         }
  462.  
  463.     /* now read the serial number and checksum block */
  464.  
  465.     if (fread (cp, 1, sizeof (sn_block_t), fp) != sizeof (sn_block_t))
  466.         {
  467.         fprintf (stderr, "\nUnable to read serial number block from file %s\n", TargetName);
  468.         return (0L);
  469.         }
  470.  
  471.     return (pos);
  472.     }
  473.  
  474. /*#page*******************************************************************
  475. *
  476. *    Seek To File Offset
  477. *
  478. *    This function will seek to the specified position in the target
  479. *    file. If the seek fails, an error message will be written to the
  480. *    console and the function will return false. If the seek succeeds,
  481. *    then TRUE is returned.
  482. *
  483. *    Synopsis:
  484. *
  485. *        rc = MoveTo (pos);
  486. *
  487. *        int rc;        true if successful seek, else false
  488. *        long pos;    desired file position
  489. *    
  490. *************************************************************************/
  491.  
  492. int MoveTo (pos)
  493. long pos;
  494.     {
  495.     if (fseek (fp, pos, SEEK_SET) != 0)
  496.         {
  497.         fprintf (stderr, "\nSeek error on file %s\n", TargetName);
  498.         return (0);
  499.         }
  500.     return (1);
  501.     }
  502.  
  503. /*#page*******************************************************************
  504. *
  505. *    Convert Serial Number to Binary
  506. *
  507. *    This function will convert the passed serial number string to
  508. *    binary and insert it into the serial number structure. It will
  509. *    return TRUE if the serial number is valid, else FALSE.
  510. *
  511. *    Synopsis:
  512. *
  513. *        rc = ConvertSerialNum (cp, sn);
  514. *
  515. *        int rc;        nonzero if valid serial number
  516. *        char *cp;    pointer to serial number string
  517. *        sn_t *sn;    pointer to serial number union
  518. *    
  519. *************************************************************************/
  520.  
  521. int ConvertSerialNum (cp, sn)
  522. char *cp;
  523. sn_t *sn;
  524.     {
  525.     char *p;
  526.  
  527.     for (p = cp; *p && isdigit (*p); ++p)
  528.         ;
  529.     if (*p)
  530.         {
  531.         fprintf (stderr, "\nSerial number contains non-numeric characters.\n");
  532.         return (0);
  533.         }
  534.  
  535.     if (strlen (cp) > 9)
  536.         {
  537.         fprintf (stderr, "\nSerial number contains too many digits.\n");
  538.         return (0);
  539.         }
  540.  
  541.     sn->sni = atol (cp);
  542.     return (1);
  543.     }
  544.  
  545. /*#page*******************************************************************
  546. *
  547. *    Convert Version Number to Binary
  548. *
  549. *    This function will convert the passed version number string to
  550. *    binary and insert it into the passed version number variable. It 
  551. *    will return TRUE if the version number is valid, else FALSE.
  552. *
  553. *    Synopsis:
  554. *
  555. *        rc = ConvertVersionNum (cp, vn);
  556. *
  557. *        int rc;            nonzero if valid version number
  558. *        char *cp;        pointer to version number string
  559. *        unsigned int *vn;    pointer to version number variable
  560. *    
  561. *************************************************************************/
  562.  
  563. int ConvertVersionNum (cp, vn)
  564. char *cp;
  565. unsigned int *vn;
  566.     {
  567.     long x;
  568.     char *p;
  569.  
  570.     for (p = cp; *p && isdigit (*p); ++p)
  571.         ;
  572.     if (*p)
  573.         {
  574.         fprintf (stderr, "\nVersion number contains non-numeric characters.\n");
  575.         return (0);
  576.         }
  577.  
  578.     if (strlen (cp) > 5)
  579.         {
  580.         fprintf (stderr, "\nVersion number contains too many digits.\n");
  581.         return (0);
  582.         }
  583.  
  584.     if ((x = atol (cp)) > UINT_MAX)
  585.         {
  586.         fprintf (stderr, "\nVersion number is greater than %u.\n", UINT_MAX);
  587.         return (0);
  588.         }
  589.  
  590.     *vn = (unsigned int) x;
  591.     return (1);
  592.     }
  593.  
  594. /*#page*******************************************************************
  595. *
  596. *    Verify and Save Pathname
  597. *
  598. *    This function will verify that the passed pathname either has no
  599. *    extension or has an .EXE extension. If there is no extension, then
  600. *    .EXE will be appended.
  601. *
  602. *    Synopsis:
  603. *
  604. *        rc = ConvertPathName (src, dst);
  605. *
  606. *        int rc;        true if valid pathname
  607. *        char *src;    source pathname
  608. *        char *dst;    destination buffer
  609. *    
  610. *************************************************************************/
  611.  
  612. int ConvertPathName (src, dst)
  613. char *src, *dst;
  614.     {
  615.     char *cp;
  616.     static char exe_ext [] = { ".EXE" };
  617.  
  618.     strcpy (dst, src);
  619.     strupr (dst);
  620.     if ((cp = strchr (src, exe_ext [0])) == NULL)
  621.         strcat (dst, exe_ext);
  622.     else if (strnicmp (cp, exe_ext, strlen (exe_ext)) != 0)
  623.         {
  624.         fprintf (stderr, "\n%s is not a valid pathname.\n", dst);
  625.         return (0);
  626.         }
  627.     return (1);
  628.     }
  629.  
  630. /*#page*******************************************************************
  631. *
  632. *    Locate Copyright Message
  633. *
  634. *    This function will search the target .EXE file for the copyright
  635. *    message pointed to by _CPPTR(). If found, it returns the offset
  636. *    in the file; otherwise, it returns NULL. The file pointer is
  637. *    a global variable.
  638. *
  639. *    Synopsis:
  640. *
  641. *        pos = LocateCopyright ();
  642. *
  643. *        long pos;    file position or NULL if not found
  644. *    
  645. *************************************************************************/
  646.  
  647. long LocateCopyright ()
  648.     {
  649.     exe_hdr_t Header;
  650.     char far *p, far *cp;
  651.     long pos;
  652.     int c;
  653.  
  654.     /* read .EXE header */
  655.  
  656.     if (fread ((char *) &Header, 1, sizeof (Header), fp) != sizeof (Header))
  657.         {
  658.         fprintf (stderr, "Cannot read .EXE file header\n");
  659.         return (0L);
  660.         }
  661.  
  662.     /* determine .EXE header size and step over it */
  663.  
  664.     if (! MoveTo (16L * (long) Header.HeaderParagraphs))
  665.         return (0L);
  666.  
  667.     /* search through program file to find the copyright message */
  668.  
  669.     for (p = _CPPTR (); ! feof (fp); )
  670.         {
  671.         while ((c = fgetc (fp)) != EOF && (char) c != *p)
  672.             ;
  673.         if (c != EOF)
  674.             {
  675.             /* First character match. Save current file position and
  676.                see if copyright found */
  677.  
  678.             pos = ftell (fp);
  679.  
  680.             for (cp = p + 1; *cp && (c = fgetc (fp)) != EOF && *cp == (char) c; ++cp)
  681.                 ;
  682.             
  683.             if (*cp)
  684.                 {
  685.                 /* mismatch - recover position and continue search */
  686.                 
  687.                 if (! MoveTo (pos))
  688.                     return (0L);
  689.                 }
  690.             else
  691.                 {
  692.                 /* found - return position */
  693.  
  694.                 return (pos - 1L);
  695.                 }
  696.             }
  697.         }
  698.  
  699.     fprintf (stderr, "\nCopyright message not found in file %s\n", TargetName);
  700.     return (0L);
  701.     }
  702.  
  703.